/**
 * 
 * 起迪科技 Copyright (c) 2014-2018 QiDi,Inc.All Rights Reserved.
 */
package cn.qidisoft.edu.hzjt.annotation;

import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.NamedThreadLocal;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;

import cn.qidisoft.core.common.log.LoggerAdapter;
import cn.qidisoft.core.common.log.LoggerAdapterFactory;
import cn.qidisoft.core.util.DateUtil;
import cn.qidisoft.edu.hzjt.constants.Constant;
import cn.qidisoft.edu.hzjt.model.Student;
import cn.qidisoft.edu.hzjt.model.SysLog;
import cn.qidisoft.edu.hzjt.model.UserEmployee;
import cn.qidisoft.edu.hzjt.service.ILoggerService;
import cn.qidisoft.edu.hzjt.utils.LogType;

/**
 * 系统日志切点类
 * 
 * @author hxq
 * @version $Id: SystemLogAspect.java, v 0.1 2018年3月2日 上午11:43:54 hxq Exp $
 */
@Aspect
@Component
public class SystemLogAspect {

  protected static final LoggerAdapter LOGGER =
      LoggerAdapterFactory.getLogger(LogType.HZJT_LOGGER.val);
  private static final ThreadLocal<Date> beginTimeThreadLocal =
      new NamedThreadLocal<Date>("ThreadLocal beginTime");
  private static final ThreadLocal<SysLog> logThreadLocal =
      new NamedThreadLocal<SysLog>("ThreadLocal log");
  private static final ThreadLocal<Object> currentUser =
      new NamedThreadLocal<Object>("ThreadLocal user");
  @Autowired
  private ThreadPoolTaskExecutor threadPoolTaskExecutor;
  @Autowired(required = false)
  private HttpServletRequest request;
  @Autowired
  private ILoggerService loggerService;

  /**
   * Controller层切点 注解拦截
   */
  @Pointcut("@annotation(cn.qidisoft.edu.hzjt.annotation.SysLogger)")
  public void controllerAspect() {}


  /**
   * 方法规则拦截
   */
  @Pointcut("execution(* cn.qidisoft.edu.hzjt.controller.*.*(..))")
  public void controllerPointerCut() {}


  /**
   * 前置通知 用于拦截Controller层记录用户的操作的开始时间
   * 
   * @param joinPoint 切点
   * @throws InterruptedException
   */
  @Before("controllerAspect()")
  public void doBefore(JoinPoint joinPoint) throws InterruptedException {
    Date beginTime = new Date();
    beginTimeThreadLocal.set(beginTime);
    // debug模式下 显式打印开始时间用于调试
    if (LOGGER.isDebugEnabled()) {
      LOGGER.debug("开始计时: {}  URI: {}",
          new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(beginTime),
          request.getRequestURI());
    }
    // 读取session中的用户
    HttpSession session = request.getSession();
    Integer loginType = (Integer) session.getAttribute(Constant.LOGIN_USER_TYPE);
    if (loginType == 2) { // 老师
      UserEmployee user = (UserEmployee) session.getAttribute(Constant.LOGIN_CONSOLE_USER);
      currentUser.set(user);
    } else {
      Student user = (Student) session.getAttribute(Constant.LOGIN_CONSOLE_USER);
      currentUser.set(user);
    }
  }

  /**
   * 后置通知 用于拦截Controller层记录用户的操作
   * 
   * @param joinPoint 切点
   * @throws Exception
   */
  @SuppressWarnings("unchecked")
  @After("controllerAspect()")
  public void doAfter(JoinPoint joinPoint) throws Exception {
    Integer userId = null;
    HttpSession session = request.getSession();
    Integer loginType = (Integer) session.getAttribute(Constant.LOGIN_USER_TYPE);
    if (loginType == 2) { // 老师
      UserEmployee user = (UserEmployee) currentUser.get();
      // 登入login操作 前置通知时用户未校验 所以session中不存在用户信息
      if (user == null) {
        user = (UserEmployee) session.getAttribute(Constant.LOGIN_CONSOLE_USER);
        if (user == null) {
          return;
        }
      } else {
        userId = user.getUid();
      }
    } else {
      Student user = (Student) currentUser.get();
      // 登入login操作 前置通知时用户未校验 所以session中不存在用户信息
      if (user == null) {
        user = (Student) session.getAttribute(Constant.LOGIN_CONSOLE_USER);
        if (user == null) {
          return;
        }
      } else {
        userId = user.getXsid();
      }
    }
    Object[] args = joinPoint.getArgs();
    String title = ""; // 主题
    String operateName = ""; // 功能
    String remark = ""; // 内容
    String type = "info"; // 日志类型(info:入库,error:错误)
    String remoteAddr = request.getRemoteAddr();// 请求的IP
    String requestUri = request.getRequestURI();// 请求的Uri
    String method = request.getMethod(); // 请求的方法类型(post/get)
    Map<String, String[]> params = request.getParameterMap(); // 请求提交的参数
    try {
      // title = getControllerMethodDescription(joinPoint);
      Map<String, String> map = getControllerMethodDescription(joinPoint);
      title = map.get("title") + "";
      operateName = map.get("opertateName") + "";
      remark = map.get("remark") + "";
    } catch (Exception e) {
      e.printStackTrace();
    }
    // debu模式下打印JVM信息。
    long beginTime = beginTimeThreadLocal.get().getTime();// 得到线程绑定的局部变量（开始时间）
    long endTime = System.currentTimeMillis(); // 2、结束时间
    if (LOGGER.isDebugEnabled()) {
      LOGGER.debug(
          "计时结束：{}  URI: {}  耗时： {}   最大内存: {}m  已分配内存: {}m  已分配内存中的剩余空间: {}m  最大可用内存: {}m",
          new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(endTime), request.getRequestURI(),
          DateUtil.formatDateTime(endTime - beginTime),
          Runtime.getRuntime().maxMemory() / 1024 / 1024,
          Runtime.getRuntime().totalMemory() / 1024 / 1024,
          Runtime.getRuntime().freeMemory() / 1024 / 1024,
          (Runtime.getRuntime().maxMemory() - Runtime.getRuntime().totalMemory()
              + Runtime.getRuntime().freeMemory()) / 1024 / 1024);
    }
    // Logger loggerReq = new Logger();
    SysLog loggerReq = new SysLog();
    loggerReq.setOperator(userId);
    loggerReq.setIphost(remoteAddr);
    loggerReq.setTarget(title);
    loggerReq.setBehaviour(operateName);
    loggerReq.setDetailinfo(remark);
    Date operateDate = beginTimeThreadLocal.get();
    loggerReq.setLogtime(operateDate);
    // loggerReq.setTitle(title);
    // loggerReq.setOperateName(operateName);
    // loggerReq.setRemark(remark);
    // loggerReq.setType(type);
    // loggerReq.setRemoteaddr(remoteAddr);
    // loggerReq.setRequesturi(requestUri);
    // loggerReq.setMethod(method);
    // loggerReq.setMapToParams(params);
    // loggerReq.setUserId(user.getUid());
    // Date operateDate = beginTimeThreadLocal.get();
    // loggerReq.setOperatedate(operateDate);
    // loggerReq.setTimeout(DateUtil.formatDateTime(endTime - beginTime));
    // 处理方式有多种，可以一下几种
    // 1.直接执行保存操作
    // loggerFacade.addLogger(loggerReq);

    // 2.优化:异步保存日志
    // new SaveLogThread(loggerReq, loggerFacade).start();

    // 3.再优化:通过线程池来执行日志保存
    threadPoolTaskExecutor.execute(new SaveLogThread(loggerReq, loggerService));
    logThreadLocal.set(loggerReq);
  }

  /**
   * 异常通知
   * 
   * @param joinPoint
   * @param e
   * @throws Exception
   */
  @AfterThrowing(pointcut = "controllerAspect()", throwing = "e")
  public void doAfterThrowing(JoinPoint joinPoint, Throwable e) throws Exception {
    SysLog loggerReq = logThreadLocal.get();
    if (loggerReq != null) {
      // loggerReq.setType("error");
      // loggerReq.setException(e.toString());
      // new UpdateLogThread(loggerReq, loggerFacade).start();
      threadPoolTaskExecutor.execute(new UpdateLogThread(loggerReq, loggerService));
    }
  }

  /**
   * 获取注解中对方法的描述信息 用于Controller层注解
   * 
   * @param joinPoint 切点
   * @return 方法描述
   */
  public static Map<String, String> getControllerMethodDescription(JoinPoint joinPoint) {
    MethodSignature signature = (MethodSignature) joinPoint.getSignature();
    Method method = signature.getMethod();
    SysLogger controllerLog = method.getAnnotation(SysLogger.class);
    HashMap<String, String> map = new HashMap<String, String>();
    String discription = controllerLog.description();
    map.put("remark", discription == null ? "" : discription);
    String title = controllerLog.moduleName();
    map.put("title", title == null ? "" : title);
    String opertateName = controllerLog.method();
    map.put("opertateName", opertateName == null ? "" : opertateName);
    return map;
  }

  /**
   * 添加日志信息线程
   */
  private static class SaveLogThread implements Runnable {
    private SysLog loggerReq;
    private ILoggerService loggerService;

    public SaveLogThread(SysLog loggerReq, ILoggerService loggerService) {
      this.loggerReq = loggerReq;
      this.loggerService = loggerService;
    }

    /**
     * @see java.lang.Runnable#run()
     */
    @Override
    public void run() {
      loggerService.addLogger(loggerReq);
    }
  }
  /**
   * 更新日志信息线程
   */
  private static class UpdateLogThread extends Thread {
    private SysLog loggerReq;
    private ILoggerService loggerService;

    public UpdateLogThread(SysLog loggerReq, ILoggerService loggerService) {
      this.loggerReq = loggerReq;
      this.loggerService = loggerService;
    }

    @Override
    public void run() {
      loggerService.updateLogger(loggerReq);
    }

  }

}
